#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <syserr.h>
#include <device.h>
#include <utils.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>
#include "dlist.h"
#include "device.h"
#include "netcore.h"
#include "evtloop.h"
#include "dev/stream.h"
#include "printk.h"

#define TCPSVR_BACKLOG      5
#define TCPSVR_PEER_NR      4

#define to_svr(stream_dev) container_of(stream_dev, struct tcp_svr_device, stream_dev)

/*
 * peer
 */
struct tcp_peer
{
    struct stream_device    dev;
    ip_addr_t               ip;
    u16                     port;
};

struct tcp_svr_device
{
    struct stream_device    stream_dev;
    struct tcp_svr_cfg      cfg;
    struct tcp_peer         peer[TCPSVR_PEER_NR];

    u32                     cache_size;
    u32                     txcache_size;
};

static struct tcp_peer *alloc_peer(struct tcp_svr_device *svr)
{
    int i;

    for (i = 0; i < array_size(svr->peer); i++)
    {
        struct tcp_peer *peer = &svr->peer[i];

        if (!ev__is_active(to_dev(&peer->dev)))
        {
            peer->dev.rxcache_size = svr->cache_size;
            peer->dev.txcache_size = svr->txcache_size;
            return peer;
        }
    }
    return NULL;
}

static int tcp_svr_new_conn(struct stream_device *stream_dev)
{
    device_t *dev = to_dev(stream_dev);
    struct tcp_svr_device *svr = to_svr(stream_dev);

    struct tcp_peer *peer = alloc_peer(svr);
    if (!peer) return -ENFILE;

    /* do accept
     */
    int peerfd;
    struct stream_device *stream_peer = &peer->dev;
    if ((peerfd = net_accept(dev_fd(dev), &peer->ip, &peer->port)) < 0)
        return -ENFILE;

    net_nonblock(peerfd, 1);
    ev__io_init(&stream_peer->dev.io, device_io, peerfd);
    ev__io_start(dev_loop(dev), &stream_peer->dev.io, EPOLLIN);
    if (dev->oflag & DEV_OFLAG_SIGNAL)
        dev_set_faynsc(peerfd, 1);
    device_open(&stream_peer->dev, dev_loop(dev), dev->oflag);

    list_add(&stream_peer->dev.subdev_entry, &dev->subdev_entry);
    stream_peer->dev.master = dev;
    dev->proxy = &stream_peer->dev;
    dev_status(dev) = DEVICE_STATUS_CONNECTED;
    dev_status(&stream_peer->dev) = DEVICE_STATUS_CONNECTED;

    if (dev->usr_cb && dev->usr_cb->ev_cb)
    {
        dev_conn_evt_args_t arg;

        arg.err = 0;
        ip_addr_copy(arg.ip, peer->ip);
        arg.port    = peer->port;
        arg.peer    = &stream_peer->dev;

        DEV_PD("<%s>: call user conn_cb...\n", dev_name(dev));
        dev->usr_cb->ev_cb(dev, DE_CONN, &arg);
    }

    DEV_PI("<%s>: got [%s:%d] conn, done!\n", dev_name(dev),
           ipaddr_ntoa(&peer->ip), peer->port);

    return 0;
}

static void tcp_svr_client_close(struct stream_device *stream_dev)
{
    device_t *dev = to_dev(stream_dev);
    struct tcp_peer *peer =  container_of(stream_dev, struct tcp_peer, dev);
    device_t *dev_master = dev->master;

    DEV_PI("<%s>: got disconn req...\n", dev_name(dev));
    dev_master->proxy = NULL;
    ev__handle_stop(dev);

    if (dev->usr_cb && dev->usr_cb->ev_cb)
    {
        dev_conn_evt_args_t arg;

        arg.err = 0;
        ip_addr_copy(arg.ip, peer->ip);
        arg.port = peer->port;
        arg.peer = dev;

        DEV_PD("<%s>: call user disconn_cb...\n", dev_name(dev));
        dev->usr_cb->ev_cb(dev, DE_DISCONN, &arg);

        dev_master->usr_cb->ev_cb(dev_master, DE_DISCONN, &arg);
    }
    device_close(dev);
}
static void tcp_svr_client_eof(struct stream_device *stream_dev)
{
    tcp_svr_client_close(stream_dev);
}

static const struct stream_operations tcp_peer_ops =
{
    .on_eof = tcp_svr_client_eof,
};

static int tcp_svr_init(struct stream_device *stream_dev)
{
    int i;
    struct tcp_svr_device *svr = to_svr(stream_dev);
    device_t *dev = to_dev(stream_dev);

    for (i = 0; i < array_size(svr->peer); i++)
    {
        char name[OBJECT_NAME_SIZE + 10];
        struct stream_device *peer_dev = &svr->peer[i].dev;

        memcpy(peer_dev, stream_dev, sizeof(struct stream_device));
        peer_dev->ops = &tcp_peer_ops;
        snprintf(name, sizeof(name), "%s_%02d", dev_name(dev), i);
        stream_device_register(peer_dev, name, NULL);
    }
    svr->cache_size = stream_dev->rxcache_size;
    stream_dev->rxcache_size = 0;
    svr->txcache_size = stream_dev->txcache_size;
    stream_dev->txcache_size = 0;
    return 0;
}
static void tcp_svr_release(struct stream_device *stream_dev)
{
    int i;
    struct tcp_svr_device *svr = to_svr(stream_dev);

    for (i = 0; i < array_size(svr->peer); i++)
        stream_device_unregister(&svr->peer[i].dev);
}

static void tcp_svr_on_poll_in(struct stream_device *stream_dev)
{
    tcp_svr_new_conn(stream_dev);
}

static int tcp_svr_open(struct stream_device *stream_dev)
{
    int sock;
    device_t *dev = to_dev(stream_dev);
    struct tcp_svr_device *svr = to_svr(stream_dev);
    struct tcp_svr_cfg *cfg = &svr->cfg;

    DEV_PD("<%s>: new socket, dev[%s], ip[%s], port[%d]...\n",
           dev_name(dev), cfg->ethname ? cfg->ethname : "default",
           ipaddr_ntoa(&cfg->local_ip), cfg->local_port);

    sock = socket_new(dev, cfg->ethname, SOCK_STREAM, &cfg->local_ip, cfg->local_port);
    if (sock < 0)
        return -ENXIO;

    if (listen(sock, TCPSVR_BACKLOG) < 0)
        goto fail;

    ev__io_init(&dev->io, device_io, sock);
    ev__io_start(dev_loop(dev), &dev->io, EPOLLIN); /* wait for incoming */

    return sock;
fail:
    close(sock);
    return -1;
}

static int tcp_svr_ctrl(struct stream_device *stream_dev, u32 cmd, void *args)
{
    struct tcp_svr_device *svr = to_svr(stream_dev);

    switch (cmd)
    {
    case DEV_CTRL_CMD_GET_CFG:
        memcpy(args, &svr->cfg, sizeof(struct tcp_svr_cfg));
        break;
    case DEV_CTRL_CMD_SET_CFG:
        if (ev__is_active(to_dev(stream_dev)))
            return -EIO;
        if (memcmp(&svr->cfg, args, sizeof(struct tcp_svr_cfg)))
            memcpy(&svr->cfg, args, sizeof(struct tcp_svr_cfg));
        break;
    }
    return 0;
}

static ssize_t tcp_svr_read(struct stream_device *stream_dev,
                            const ev_buf_t *buf, size_t n)
{
    DEV_PD("<%s>: no peer, return!\n", dev_name(&stream_dev->dev));
    return -EIO;
}
static ssize_t tcp_svr_write(struct stream_device *stream_dev,
                             const ev_buf_t *buf, size_t n)
{
    DEV_PD("<%s>: no peer, return!\n", dev_name(&stream_dev->dev));
    return -EIO;
}
static const struct stream_operations tcp_svr_ops =
{
    .init   = tcp_svr_init,
    .open   = tcp_svr_open,
    .ctrl   = tcp_svr_ctrl,
    .ioread = tcp_svr_read,
    .iowrite = tcp_svr_write,
    .release = tcp_svr_release,

    .on_poll_in = tcp_svr_on_poll_in,
};
static const struct tcp_svr_cfg tcp_server_cfg_def =
{
    .ethname    = "eth0",
    .local_ip   = IPADDR4_INIT(0),
    .local_port = 0,
};
int tcp_svr_dev_add(const char *name, const struct tcp_svr_cfg *cfg)
{
    int ret;
    struct tcp_svr_device *svr = kmalloc(sizeof(struct tcp_svr_device));

    if (!svr)
        return -ENOMEM;

    memset(svr, 0, sizeof(struct tcp_svr_device));
    if (!cfg) cfg = &tcp_server_cfg_def;
    memcpy(&svr->cfg, cfg, sizeof(struct tcp_svr_cfg));

    svr->stream_dev.ops = &tcp_svr_ops;

    ret = stream_device_register(&svr->stream_dev, name, NULL);
    if (ret < 0)
        kfree(svr);

    return ret;
}

void tcp_svr_del(device_t *dev)
{
    struct stream_device *stream_dev = to_stream_dev(dev);
    struct tcp_svr_device *svr = to_svr(stream_dev);

    stream_device_unregister(stream_dev);
    kfree(svr);
}

